#include "func.h"
#define N 5//超时断开等待的秒数

int setNonblock(int fd)
{
    int status;
    status=fcntl(fd,F_GETFL);//获取文件描述符的状态
    status=status|O_NONBLOCK;//修改为非阻塞
    int ret;
    ret=fcntl(fd,F_SETFL,status);//将新状态设置回文件描述符
    ERROR_CHECK(ret,-1,"fcntl");
}

int main(int argc,char *argv[])
{
    ARGS_CHECK(argc,3);
    int sock_fd=socket(AF_INET,SOCK_STREAM,0);
    ERROR_CHECK(sock_fd,-1,"socket");
    struct sockaddr_in serAddr;
    serAddr.sin_family=AF_INET;
    serAddr.sin_addr.s_addr=inet_addr(argv[1]);
    serAddr.sin_port=htons(atoi(argv[2]));
    int ret;
    //地址重用
    int reuse=1;
    ret=setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int));
    ERROR_CHECK(ret,-1,"setsockopt");
    ret=bind(sock_fd,(struct sockaddr *)&serAddr,sizeof(serAddr));
    ERROR_CHECK(ret,-1,"bind");
    listen(sock_fd,10);

    //开始用epoll
    int epfd=epoll_create(1);
    struct epoll_event event;
    event.data.fd=STDIN_FILENO;
    event.events=EPOLLIN;
    ret=epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&event);
    ERROR_CHECK(ret,-1,"epoll_ctl");

    event.data.fd=sock_fd;
    event.events=EPOLLIN;
    ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sock_fd,&event);
    ERROR_CHECK(ret,-1,"epoll_ctl");
    struct epoll_event evs[3];

    struct sockaddr_in clientAddr;
    int addrLen,new_fd;
    int ready_fd_num,i;
    char buf[1000]={0};

    //超时控制
    time_t nowTime,lastTime;
    nowTime=lastTime=time(NULL);
    int isLogin=0;//登录标志（没有登录）
    char buf_recv[6]={0};//用于测试
    while (1)
    {
        ready_fd_num=epoll_wait(epfd,evs,3,1000);//用就等待用-1
        //超时断开
        if(0==ready_fd_num&&isLogin)
        {
            nowTime=time(NULL);
            if(nowTime-lastTime>N)
            {
                printf("The client has not sent messages for a long time and the server actively disconnected\n");
                //解除对new_fd的监控
                event.data.fd=new_fd;
                event.events=EPOLLIN;
                ret=epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
                ERROR_CHECK(ret,-1,"epoll_ctl");
                close(new_fd);
                isLogin=0;
            }
        }
        for(i=0;i<ready_fd_num;i++)
        {
            lastTime=time(NULL);
            //sock_fd可读、代表有人链接了
            if(evs[i].data.fd==sock_fd)
            {
                addrLen=sizeof(clientAddr);
                new_fd=accept(sock_fd,(struct sockaddr *)&clientAddr,&addrLen);
                ERROR_CHECK(new_fd,-1,"accept");
                printf("ip=%s,port=%d connect\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
                //修改new_fd为非阻塞
                setNonblock(new_fd);
                //将new_fd加入监听
                event.data.fd=new_fd;
                event.events=EPOLLIN|EPOLLET;//EPOLLIN：监控读时间；EPOLLET：采用边沿触发
                ret=epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);
                ERROR_CHECK(ret,-1,"epoll_ctl");
                isLogin=1;
            }else if(evs[i].data.fd==STDIN_FILENO)
            {
                memset(buf,0,sizeof(buf));
                read(STDIN_FILENO,buf,sizeof(buf));
                send(new_fd,buf,strlen(buf)-1,0);
            }else if(evs[i].data.fd==new_fd)
            {
                //任务到来，模拟子线程不停读取数据
                while(1)
                {
                    memset(buf_recv,0,sizeof(buf_recv));
                    //客户端发数据比较多，就会执行多次recv
                    ret=recv(new_fd,buf_recv,sizeof(buf_recv)-1,0);
                    if(0==ret)
                    {
                        printf("对端断开了\n");
                        //删除客户端的描述符、避免疯狂打印
                        epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,0);
                        close(new_fd);
                        isLogin=0;//客户端自己断开
                        goto end;                        
                    }else if(-1==ret)//非阻塞模式下，数据读空，返回-1
                    {
                        printf("\n");
                        break;
                    }
                    printf("%s",buf_recv);
                }
            }
        }
    end:
    }
    close(sock_fd);
    return 0;
}